package com.crawler.waf.config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

/**
 * 版本控制
 * @author rubekid
 */
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {

    // 路径中版本的前缀， 这里用 /v[1.0-999.9]/的形式
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+(\\.\\d+)?)/");
    
    private static Map<String, List<Float>> versionMap = new HashMap<String, List<Float>>();
    
    /**
     * 版本号
     */
    private float apiVersion;

    /**
     * 路径加入映射
     * @param requestMappingInfo
     */
    public synchronized static void add(RequestMappingInfo requestMappingInfo){
    	for(String key : requestMappingInfo.getPatternsCondition().getPatterns()){
			if(key.contains("{version}")){
				String method = "GET";
				if(requestMappingInfo.getMethodsCondition().getMethods().iterator().hasNext()){
					 method = requestMappingInfo.getMethodsCondition().getMethods().iterator().next().toString();
				}
				key = key +":"+ method;
				List<Float> versions = new ArrayList<Float>();
				if(versionMap.containsKey(key)){
					versions = versionMap.get(key);
				}
				float version = ((ApiVersionCondition)requestMappingInfo.getCustomCondition()).getApiVersion();
				if(!versions.contains(version)){					
					versions.add(version);
				}
				Collections.sort(versions);
				Collections.reverse(versions);
				versionMap.put(key, versions);
			}
		}
    } 
    
    /**
     * 构造函数
     * @param apiVersion
     */
    public ApiVersionCondition(float apiVersion){
        this.apiVersion = apiVersion;
    }
    
    /**
     * 构造函数
     */
    public ApiVersionCondition combine(ApiVersionCondition other) {
        // 采用最后定义优先原则，则方法上的定义覆盖类上面的定义
        return new ApiVersionCondition(other.getApiVersion());
    }
    
    /**
     * 获取配置项
     */
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
    	String path = request.getPathInfo()  != null ? request.getPathInfo() : request.getServletPath();
        Matcher m = VERSION_PREFIX_PATTERN.matcher(path);
        if(m.find()){
            Float version = Float.valueOf(m.group(1));
            if(version == apiVersion){
            	return this;
            }
            else if(version > apiVersion){ //大于配置版本号是，要判断配置的版本号是否最接近请求版本号
            	String key = path.replaceAll("v(\\d+(\\.\\d+)?)/" , "v{version}/") +":" + request.getMethod();
            	List<Float> versions = match(key);
                if(versions != null && versions.size() > 0){
                	int length = versions.size();
                	for(int i=0; i<length; i++){
                		Float upper = Float.MAX_VALUE;
                		Float lower = versions.get(i);
                		if(i > 0){
                			upper = versions.get(i-1);
                		}
                		// 如果请求的版本号大于配置版本号， 则满足
                        if(version >= lower && version < upper && apiVersion == lower){
                        	return this;
                        }
                	}
                	
                }
            }
            
            
        }
        return null;
    }

    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
        // 优先匹配最新的版本号
        return other.getApiVersion() - this.apiVersion > 0 ? 1 : -1;
    }

    public float getApiVersion() {
        return apiVersion;
    }
    
    @SuppressWarnings("unchecked")
	public List<Float> match(String key){
    	List<JSONObject> matchArray = new ArrayList<JSONObject>();
    	for(Map.Entry<String, List<Float>> entry : versionMap.entrySet()){
    		String regex = "^" +  entry.getKey().replace("v{version}", "VERSION_PLACEHOLD").replaceAll("\\{[\\w\\.-]+\\}", "[\\\\w\\\\.-]+").replace("VERSION_PLACEHOLD", "v\\{version\\}") + "$";
    		
    		if(key.matches(regex)){
    			int placeholdCount = regex.split("\\[\\\\w\\\\.-\\]\\+").length -1;
    			JSONObject match = new JSONObject();
    			match.put("key", entry.getKey());
    			match.put("value", entry.getValue());
    			match.put("flag", placeholdCount);
    			matchArray.add(match);
    			
    		}
    	}
    	if(matchArray.size() > 0){
    		if(matchArray.size() > 1){
    			Collections.sort(matchArray, new Comparator<JSONObject>() {
					@Override
					public int compare(JSONObject o1, JSONObject o2) {
						return o1.getInteger("flag") - o2.getInteger("flag");
					}
				});
    		}
    		return (List<Float>) matchArray.get(0).get("value");
    	}
    	return null;
    }


}